package com.qf.cx.simplenms.reader.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.qf.cx.simplenms.common.utils.StringUtils;
import com.qf.cx.simplenms.link.api.LinkManager;
import com.qf.cx.simplenms.link.model.AbstractOpResultSpec;
import com.qf.cx.simplenms.link.model.OpSpec;
import com.qf.cx.simplenms.link.model.ReadCustomResultSpec;
import com.qf.cx.simplenms.link.model.ReadOpResultSpec;
import com.qf.cx.simplenms.link.model.ReadOpSpec;
import com.qf.cx.simplenms.link.model.TagResultSpec;
import com.qf.cx.simplenms.reader.model.ReaderModel;
import com.qf.cx.simplenms.reader.tag.Tag;
import com.qf.cx.simplenms.reader.tag.Tag.VehicleInfo;
import com.qf.cx.simplenms.tagmemory.model.TagMemoryModel;
import com.qf.cx.simplenms.tagmemory.model.TagMemoryModel.Identifier;

public abstract class CommonReader extends AbstractReader {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	private final Map<Byte, List<TagMemoryModel>> opId2TagMemory = new ConcurrentHashMap<>();

	public CommonReader(ReaderModel readerModel, LinkManager linkManager) {
		super(readerModel, linkManager);
	}

	@Override
	protected OpSpec createOpspec() {
		OpSpec opSpec = new OpSpec();

		this.readerModel.getAntennas().forEach(t -> opSpec.addAntenna(t.getAntennaNo()));

		Map<Identifier, List<TagMemoryModel>> unique = new LinkedHashMap<>();

		for (TagMemoryModel each : generateUniqueTagMemories()) {
			Identifier key = each.indentifier();
			unique.putIfAbsent(key, new ArrayList<>());
			List<TagMemoryModel> list = unique.get(key);
			list.add(each);
		}
		byte opId = 1;
		opId2TagMemory.clear();
		for (List<TagMemoryModel> tms : unique.values()) {
			ReadOpSpec spec = new ReadOpSpec();
			TagMemoryModel tagMemoryModel = tms.get(0);
			spec.setOpId(opId);
			spec.setLen((byte) tagMemoryModel.length);
			spec.setMb((byte) tagMemoryModel.bank);
			spec.setOffset((byte) tagMemoryModel.offset);
			opId2TagMemory.put(opId++, tms);
			logger.debug("generate reader op spec " + spec);
			opSpec.addRwOpSpec(spec);
		}
		return opSpec;
	}

	@Override
	protected Tag generateTag(TagResultSpec tagResultSpec) {
		Tag tag = new Tag();
		tag.setTid(StringUtils.bytesToHex(tagResultSpec.getTidData()));
		tag.setDiscoveryTime(tagResultSpec.getSeenTimeUTC());
		Map<Identifier, Tag.ReadOpResult> readOpResults = new HashMap<>();

		for (AbstractOpResultSpec opResult : tagResultSpec.getOpResult()) {
			List<TagMemoryModel> tagMemoryModels = opId2TagMemory.get(opResult.getOpId());
			if (tagMemoryModels == null || tagMemoryModels.isEmpty()) {
				logger.error("no op id = {}", opResult.getOpId());
				continue;
			}

			if (opResult instanceof ReadOpResultSpec) {
				ReadOpResultSpec result = (ReadOpResultSpec) opResult;
				Tag.ReadOpResult readOpResult = new Tag.ReadOpResult();
				readOpResult.setData(StringUtils.shortsToHex(result.getData(), result.getLen()));
				readOpResult.setOpResult(result.getResult());

				for (TagMemoryModel tagMemoryModel : tagMemoryModels) {
					readOpResults.putIfAbsent(tagMemoryModel.indentifier(), readOpResult);
				}
			} else if (opResult instanceof ReadCustomResultSpec) {
				ReadCustomResultSpec result = (ReadCustomResultSpec) opResult;
				VehicleInfo v = VehicleInfo.from(result);
				for (TagMemoryModel tagMemoryModel : tagMemoryModels) {
					tag.addVehicleInfo(tagMemoryModel.indentifier(), v);
				}
			} else {
				logger.error("can't handle op result type of {}", opResult.getClass());
			}
		}
		tag.setReadOpResults(readOpResults);
		return tag;
	}

	protected List<TagMemoryModel> generateUniqueTagMemories() {
		// 保证标签区区只下发一次
		Set<TagMemoryModel> set = new HashSet<>();
		observer2TagMemories.values().forEach(t -> set.addAll(t));
		List<TagMemoryModel> list = new ArrayList<>(set);
		return list;
	}
}
